#pragma once

#include "Core.h"
#include "Texture.h"

namespace Gut
{
	struct RenderBuffer
	{

		unsigned int ID;

		void Create()
		{
			glGenRenderbuffers(1, &ID);
		}

		~RenderBuffer()
		{
			glDeleteRenderbuffers(1, &ID);
		}
		void Bind()
		{
			glBindRenderbuffer(GL_RENDERBUFFER, ID);
		}

		void Storage(unsigned int internalFormat, int width, int height)
		{
			glBindRenderbuffer(GL_RENDERBUFFER, ID);
			glRenderbufferStorage(GL_RENDERBUFFER, internalFormat, width, height);
			glBindRenderbuffer(GL_RENDERBUFFER, 0);
		}
		void StorageMultiSample(unsigned int samples, unsigned int internalFormat, int width, int height)
		{
			glBindRenderbuffer(GL_RENDERBUFFER, ID);
			glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalFormat, width, height);
		}
	};

	struct FrameBuffer
	{
		unsigned int FBO;

		void BufferTextureLayer(unsigned int attachment, unsigned int texId, int level, int layer)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, texId, level, layer);
		}

		void BufferTexture2D(unsigned int attachment, unsigned int texTarget, unsigned int ID, unsigned int mipLevel = 0)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, texTarget, ID, mipLevel);
		}

		void BufferTextureLayer(unsigned int attachment, unsigned int tex, unsigned int mipLevel, unsigned int zOff)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, tex, mipLevel, zOff);
		}

		void RenderBuffer(unsigned int attachment, unsigned int RBO)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glBindRenderbuffer(GL_RENDERBUFFER, RBO);
			glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, RBO);
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}
		void ReadBuffer(unsigned int attachment)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, this->FBO);
			glReadBuffer(attachment);
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}

		void DrawBuffers(unsigned int *attachments, int num)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glDrawBuffers(num, attachments);
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}
		void Create()
		{
			glDeleteFramebuffers(1, &FBO);
			glGenFramebuffers(1, &FBO);
		}

		void CheckError()
		{
			// - Finally check if framebuffer is complete
			if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
				LOGI("Framebuffer not complete!");
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}

		void Bind()
		{
			glBindFramebuffer(GL_FRAMEBUFFER, this->FBO);
		}

#ifdef WIN32

		void BufferTexture(unsigned int attachment, unsigned int texId, int level)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glFramebufferTexture(GL_FRAMEBUFFER, attachment, texId, level);
		}

		void BufferTexture3D(unsigned int attachment, unsigned int texTarget, unsigned int tex, unsigned int mipLevel, unsigned int zOff)
		{
			glBindFramebuffer(GL_FRAMEBUFFER, FBO);
			glFramebufferTexture3D(GL_FRAMEBUFFER, attachment, texTarget, tex, mipLevel, zOff);
		}
#endif // DEBUG

		~FrameBuffer()
		{
			if (FBO)
			{
				glDeleteFramebuffers(1, &FBO);
				FBO = 0;
			}
		}
		static void UnBind()
		{
			glBindFramebuffer(GL_FRAMEBUFFER, 0);
		}
	};

	// struct DepthMapDesc
	//{
	//	int width, height;
	//
	// };
	//
	// struct DepthMap
	//{
	//	unsigned int ID;
	//	int width, height;
	//	bool CreateFromDesc(const DepthMapDesc& desc)
	//	{
	//		glGenFramebuffers(1, &ID);
	//		// create depth texture
	//		unsigned int depthMap;
	//		glGenTextures(1, &depthMap);
	//		glBindTexture(GL_TEXTURE_2D, depthMap);
	//		glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
	//			desc.width, desc.height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
	//		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	//		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	//		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	//		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	//		// attach depth texture as FBO's depth buffer
	//		glBindFramebuffer(GL_FRAMEBUFFER, ID);
	//		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
	//		glDrawBuffer(GL_NONE);
	//		glReadBuffer(GL_NONE);
	//		glBindFramebuffer(GL_FRAMEBUFFER, 0);
	//
	//		return true;
	//	}
	//	void Bind()
	//	{
	//		glBindFramebuffer(GL_FRAMEBUFFER, this->ID);
	//		glViewport(0, 0, width, height);
	//	}
	//	void UnBind()
	//	{
	//		glBindFramebuffer(GL_FRAMEBUFFER, 0);
	//	}
	// };

}